/* Copyright (c) 2007, Jan Flora <janflora@diku.dk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the University of Copenhagen nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
  @author Jan Flora <janflora@diku.dk>
*/

#include <macTypes.h>
#include <macConstants.h>
#include <Int64Compare.h>

module pongM
{
	provides
	{
		interface StdControl;
	}
	uses
	{
	    interface IeeeMacPibAttribute as IeeePibAttribute;
	    interface IeeePanDescriptor;
		interface IeeeMacSdu as IeeeSdu;
		interface IeeeAddress;
	   
		interface McpsDataIndication;
		interface McpsDataRequestConfirm;
	
		interface MlmeRxEnableRequestConfirm;
		
		// MCPS
		interface IeeeIndication<Mcps_DataIndication> as McpsIndicationData;   	
		interface IeeeRequestConfirm<Mcps_DataRequestConfirm> as McpsRequestConfirmData;

		// MLME
		interface IeeeRequestConfirm<Mlme_RxEnableRequestConfirm> as MlmeRequestConfirmRxEnable;

		interface Debug;
	}
}
implementation
{
	//#define DBG_OFF 0
	#define DBG_LEVEL 1
	#include "Debug.h"
	
	#ifdef PROFILING
		// Set up profiling pins.
		TOSH_ASSIGN_PIN(PROFILING1, C, 5);
		TOSH_ASSIGN_PIN(PROFILING2, C, 6);
		TOSH_ASSIGN_PIN(PROFILING3, C, 7);
		TOSH_ASSIGN_PIN(PROFILING4, G, 3);
		TOSH_ASSIGN_PIN(PROFILING5, G, 1);
		TOSH_ASSIGN_PIN(PROFILING6, B, 7);
		TOSH_ASSIGN_PIN(PROFILING7, B, 3);
		TOSH_ASSIGN_PIN(PROFILING8, B, 2);
		TOSH_ASSIGN_PIN(PROFILING9, B, 1);
	#endif
	
	uint8_t myExtAddress[8] = {0x00, 0x50, 0xC2, 0x37, 0xB0, 0x01, 0x03, 0x61};
	
	#define RX_ON  1
	#define RX_OFF 2

	uint8_t myMsduHandle = 0x01;
	Ieee_Address pingReplyAddr;
	uint8_t rxEnableType = 0;
	
	// Forward declarations of helper functions.
	task void pingReply();
	task void rxOn();
	task void rxOff();
	int strcmp(const char * a, const char * b);
	
	command result_t StdControl.init()
	{
		#ifdef PROFILING
			// Setup profiling pins as output pins.
			TOSH_MAKE_PROFILING1_OUTPUT();
			TOSH_MAKE_PROFILING2_OUTPUT();
			TOSH_MAKE_PROFILING3_OUTPUT();
			TOSH_MAKE_PROFILING4_OUTPUT();
			TOSH_MAKE_PROFILING5_OUTPUT();
			TOSH_MAKE_PROFILING6_OUTPUT();
			TOSH_MAKE_PROFILING7_OUTPUT();
			TOSH_MAKE_PROFILING8_OUTPUT();
			TOSH_MAKE_PROFILING9_OUTPUT();
			
			// Clear all pins.
			TOSH_CLR_PROFILING1_PIN();
			TOSH_CLR_PROFILING2_PIN();
			TOSH_CLR_PROFILING3_PIN();
			TOSH_CLR_PROFILING4_PIN();
			TOSH_CLR_PROFILING5_PIN();
			TOSH_CLR_PROFILING6_PIN();
			TOSH_CLR_PROFILING7_PIN();
			TOSH_CLR_PROFILING8_PIN();
			TOSH_CLR_PROFILING9_PIN();
		#endif

		call IeeeAddress.create(&pingReplyAddr);
		return SUCCESS;
	}

	command result_t StdControl.start()
	{
		uint8_t myAddr[8] = {0x61, 0x03, 0x01, 0xB0, 0x37, 0xC2, 0x50, 0x00};
		memcpy(aExtendedAddress, myAddr, 8);
		post rxOn();
		return SUCCESS;
	}

	command result_t StdControl.stop()
	{
		return FAIL;
	}
	
	/*******************************
	  Data handling        
	 *******************************/
	
	event void McpsIndicationData.indication(Mcps_DataIndication indication)
	{
		Ieee_Msdu msdu;
		char *payload;
		uint8_t payloadLen;
		
		#ifdef PROFILING
			TOSH_SET_PROFILING1_PIN();
		#endif
		
		call IeeeSdu.create(&msdu);
		call McpsDataIndication.getMsdu(indication, msdu);
		payload = (char*)call IeeeSdu.getPayload(msdu);
		payloadLen = call IeeeSdu.getPayloadLen(msdu);
		
		if (payloadLen == 5 && 0 == strcmp("ping", payload)) {
			DBG_STR("Received a ping request.",1);
			call McpsDataIndication.getSrcAddress(indication, pingReplyAddr);
			post rxOff();
		} else {
			DBG_STR("Ignoring data received different from ping request.",1);
			DBG_DUMP(payload, payloadLen, 1);
		}

		call McpsDataIndication.destroy(indication);
		call IeeeSdu.destroy(msdu);
		
		#ifdef PROFILING
			TOSH_CLR_PROFILING1_PIN();
		#endif
	}

	event void McpsRequestConfirmData.confirm( Mcps_DataRequestConfirm confirm )
	{
		//uint8_t handle = call McpsDataRequestConfirm.getMsduHandle(confirm);
		Ieee_Status status = call McpsDataRequestConfirm.getStatus(confirm);
		
		if (status == IEEE802154_SUCCESS) {
			DBG_STR("Ping reply transmitted.",1);
		} else {
			DBG_STR("Ping reply failed.",1);
		}
		call McpsDataRequestConfirm.destroy(confirm);
		
		#ifdef PROFILING
			TOSH_CLR_PROFILING2_PIN();
		#endif
	}
	
	task void pingReply()
	{
		Ieee_Address srcAddr;
		Mcps_DataRequestConfirm request;
		Ieee_Msdu myMsdu;
		char *payload = "pong";
		
		#ifdef PROFILING
			TOSH_SET_PROFILING2_PIN();
		#endif
		
		if (FAIL == call IeeeAddress.create( &srcAddr )) {
			DBG_STR("Failed to allocate SrcAddr",1);
		}
		if (FAIL == call IeeeSdu.create( &myMsdu )) {
			DBG_STR("Failed to allocate SDU",1);
		}

		// Fill out the source address.
		call IeeeAddress.setAddrMode( srcAddr, 3 );
		call IeeeAddress.setAddress( srcAddr, myExtAddress );
		call IeeeAddress.setPanId( srcAddr, 0xFFFF);
    
		// Create request
		call McpsDataRequestConfirm.create(&request, srcAddr, pingReplyAddr, 5);

		call McpsDataRequestConfirm.getMsdu( request, myMsdu );
		memcpy(call IeeeSdu.getPayload( myMsdu ), payload, 5);
		call McpsDataRequestConfirm.setMsduHandle( request, myMsduHandle );
		call McpsDataRequestConfirm.setTxOptions( request, (IEEE802154_TxOptsAck) );

		if (SUCCESS == call McpsRequestConfirmData.request(request)) {
			DBG_STR("Ping reply was enqueued.",1);
			myMsduHandle++;
		} else {
			DBG_STR("Transmission of ping reply failed.",1);
		}
		
		// Clean up.
		call IeeeAddress.destroy( srcAddr );
		call IeeeSdu.destroy( myMsdu );
	}
	
	/*******************************
	  Receiver On handling        
	 *******************************/
	
	event void MlmeRequestConfirmRxEnable.confirm(Mlme_RxEnableRequestConfirm confirm)
	{
		Ieee_Status status = call MlmeRxEnableRequestConfirm.getStatus(confirm);
		
		if (status == IEEE802154_SUCCESS) {
			if (rxEnableType == RX_ON) {
				DBG_STR("Receiver was turned on",1);
			} else {
				DBG_STR("Receiver was turned off",1);
				post pingReply();
			}
		} else {
			if (rxEnableType == RX_ON) {
				DBG_STRINT("Could not turn on receiver. Error was: ",status,1);
			} else {
				DBG_STRINT("Could not turn off receiver. Error was: ",status,1);
				post pingReply();
			}
		}
		
		call MlmeRxEnableRequestConfirm.destroy(confirm);
		
		#ifdef PROFILING
			TOSH_CLR_PROFILING3_PIN();
			TOSH_CLR_PROFILING4_PIN();
		#endif
	}
	
	task void rxOn()
	{
		Mlme_RxEnableRequestConfirm request;
		
		#ifdef PROFILING
			TOSH_SET_PROFILING3_PIN();
		#endif
		
		call MlmeRxEnableRequestConfirm.create(&request);
		
		// ONLY FREESCALE
		call MlmeRxEnableRequestConfirm.setDeferPermit(request, TRUE);
		call MlmeRxEnableRequestConfirm.setRxOnTime(request,0);
		
		// Keep the receiver on for 10 seconds.
		call MlmeRxEnableRequestConfirm.setRxOnDuration(request,625000);
		rxEnableType = RX_ON;
		
		if (SUCCESS == call MlmeRequestConfirmRxEnable.request(request)) {
			DBG_STR("RxOn was requested",1);
		} else {
			DBG_STR("RxOn request failed",1);
		}
	}
	
	task void rxOff()
	{
		Mlme_RxEnableRequestConfirm request;
		
		#ifdef PROFILING
			TOSH_SET_PROFILING4_PIN();
		#endif
		
		call MlmeRxEnableRequestConfirm.create(&request);
		
		// ONLY FREESCALE
		call MlmeRxEnableRequestConfirm.setDeferPermit(request, TRUE);
		call MlmeRxEnableRequestConfirm.setRxOnTime(request,0);
		
		call MlmeRxEnableRequestConfirm.setRxOnDuration(request,0);
		rxEnableType = RX_OFF;
		
		if (SUCCESS == call MlmeRequestConfirmRxEnable.request(request)) {
			DBG_STR("RxOff was requested",1);
		} else {
			DBG_STR("RxOff request failed",1);
		}
	}
	
	
	/** Help function, does string compare */
	int strcmp(const char * a, const char * b)
	{
		while (*a && *b && *a == *b) { ++a; ++b; };
		return *a - *b;
	}
}